package cn.kai.util;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 简单时间工具 更多可使用 DateUtils
 * Author: cKnight
 * Date: 2024/6/21
 */
@Slf4j
public class TimeUtil {
    /**
     * 默认日期格式
     */
    private static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 默认日期格式
     */
    private static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_PATTERN);

    private static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();

    public static String getNow() {
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();

        // 创建一个 DateTimeFormatter 对象用于格式化日期
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

        // 将当前日期格式化为字符串
        return currentDate.format(formatter);
    }

    /**
     * LocalDateTime 转 字符串，指定日期格式
     *
     * @param pattern
     * @return
     */
    public static String format(LocalDateTime localDateTime, String pattern) {
        if (StrUtil.isBlank(pattern)) pattern = DEFAULT_PATTERN;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        String timeStr = formatter.format(localDateTime.atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }


    /**
     * Date 转 字符串, 指定日期格式
     *
     * @param time
     * @param pattern
     * @return
     */
    public static String format(Date time, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        String timeStr = formatter.format(time.toInstant().atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }

    /**
     * Date 转 字符串，默认日期格式
     *
     * @param time
     * @return
     */
    public static String format(Date time) {
        if (Objects.isNull(time)) {
            return null;
        }
        String timeStr = DEFAULT_DATE_TIME_FORMATTER.format(time.toInstant().atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }

    /**
     * timestamp 转 字符串，默认日期格式
     *
     * @return
     */
    public static String format(long timestamp) {
        String timeStr = DEFAULT_DATE_TIME_FORMATTER.format(new Date(timestamp).toInstant().atZone(DEFAULT_ZONE_ID));
        return timeStr;
    }

    /**
     * LocalDate转 字符串，指定日期格式
     *
     * @param localDate
     * @return
     */
    public static String format(LocalDate localDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return localDate.format(formatter);
    }

    /**
     * Date 转 LocalDate 或 LocalDateTime
     *
     * @param date
     * @param targetType
     * @return
     */
    public static <T> T format(Date date, Class<T> targetType) {
        if (targetType.equals(LocalDate.class)) {
            return targetType.cast(date.toInstant().atZone(DEFAULT_ZONE_ID).toLocalDate());
        } else if (targetType.equals(LocalDateTime.class)) {
            return targetType.cast(date.toInstant().atZone(DEFAULT_ZONE_ID).toLocalDateTime());
        } else {
            throw new IllegalArgumentException("Unsupported target type");
        }
    }

    /**
     * LocalDate 转  Date
     *
     * @param localDateTime
     * @return
     */
    public static Date convertToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * String转 LocalDateTime
     *
     * @param timeStr
     * @return
     */
    public static LocalDateTime format(String timeStr) {
        return LocalDateTime.parse(timeStr, DEFAULT_DATE_TIME_FORMATTER);
    }

    /**
     * cron表达式格式化
     *
     * @param date
     * @return
     */
    public static String formatCron(Date date) {
        SimpleDateFormat cronFormat = new SimpleDateFormat("ss mm HH dd MM ?");
        return cronFormat.format(date);
    }

    public static Date formatDate(String time) {
        try {
            LocalDateTime format = format(time);
            ZoneId zoneIdChina = ZoneId.of("Asia/Shanghai");
            ZonedDateTime zonedDateTime = format.atZone(zoneIdChina);
            Instant instant = zonedDateTime.toInstant();
            return Date.from(instant);
        } catch (Exception e) {
            log.error("formatDate转换异常timeStr:{}", time);
            log.error("formatDate转换异常:", e);
            return null;
        }

    }

    public static Date formatDate(String time, String formatter) {
        LocalDateTime format = LocalDateTime.parse(time, DateTimeFormatter.ofPattern(formatter));
        ZoneId zoneIdChina = ZoneId.of("Asia/Shanghai");
        ZonedDateTime zonedDateTime = format.atZone(zoneIdChina);
        Instant instant = zonedDateTime.toInstant();
        return Date.from(instant);
    }

    /**
     * 字符串 转 Date
     *
     * @param time
     * @return
     */
    public static Date strToDate(String time) {
        LocalDateTime localDateTime = LocalDateTime.parse(time, DEFAULT_DATE_TIME_FORMATTER);
        return Date.from(localDateTime.atZone(DEFAULT_ZONE_ID).toInstant());

    }


    /**
     * 获取当天剩余的秒数
     *
     * @param currentDate
     * @return
     */
    public static Long getRemainSecondsOneDay(Date currentDate) {
        LocalDateTime midnight = LocalDateTime.ofInstant(currentDate.toInstant(),
                        ZoneId.systemDefault()).plusDays(1).withHour(0).withMinute(0)
                .withSecond(0).withNano(0);
        LocalDateTime currentDateTime = LocalDateTime.ofInstant(currentDate.toInstant(),
                ZoneId.systemDefault());
        return ChronoUnit.SECONDS.between(currentDateTime, midnight);
    }

    /**
     * 获取起始日期时间和结束日期时间的时间区间集合
     * 默认包含起始和结束日期
     * 默认单位为 days
     * 默认step为 1
     *
     * @param start
     * @param end
     * @return
     */
    public static List<LocalDate> between(LocalDate start, LocalDate end) {
        long interval = start.until(end, ChronoUnit.DAYS);
        return Stream.iterate(start, e -> e.plusDays(1)).limit(interval).collect(Collectors.toList());
    }


    /**
     * 判断目标时间是否在时间内
     *
     * @param from
     * @param to
     * @param target
     * @return
     */
    public static boolean judgeBetweenTime(Date from, Date to, Date target) {
        LocalDateTime now = format(target, LocalDateTime.class);
        return now.isAfter(format(from, LocalDateTime.class)) && now.isBefore(format(to, LocalDateTime.class));
    }

    public static boolean judgeBetweenTime(Date range, Date target, DateField field) {
        switch (field) {
            case MONTH:
                return judgeBetweenTime(DateUtil.beginOfMonth(range), DateUtil.endOfMonth(range), target);
            case DAY_OF_MONTH:
                return judgeBetweenTime(DateUtil.beginOfDay(range), DateUtil.endOfDay(range), target);
            default:
                return judgeBetweenTime(DateUtil.beginOfDay(range), DateUtil.endOfDay(range), target);
        }
    }

    /**
     * 获取 月份的 天
     *
     * @param start
     * @param end
     * @return
     */
    public static Map<DateTime, List<DateTime>> rangeMonthDay(Date start, Date end) {
        Map<DateTime, List<DateTime>> result = new LinkedHashMap<>();
        List<DateTime> dateTimes = DateUtil.rangeToList(start, end, DateField.MONTH);
        if (CollectionUtils.isEmpty(dateTimes)) {
            return result;
        } else {
            for (DateTime dateTime : dateTimes) {
                List<DateTime> dayList = DateUtil.rangeToList(dateTime, DateUtil.endOfMonth(dateTime), DateField.DAY_OF_MONTH);
                result.put(dateTime, dayList);
            }
            return result;
        }
    }

    /**
     * 偏移量 月的开始
     *
     * @param offset
     * @return
     */
    public static Date offsetMonthBegin(int offset) {
        return offsetMonthBegin(new Date(), offset);
    }

    public static Date offsetMonthBegin(Date date, int offset) {
        return offsetBegin(date, offset, DateField.MONTH);
    }

    public static Date offsetBegin(Date date, int offset, DateField dateField) {
        switch (dateField) {
            case MONTH:
                return DateUtil.beginOfMonth(DateUtil.offsetMonth(date, offset));
            case YEAR:
                return DateUtil.beginOfYear(DateUtil.offset(date, dateField, offset));
            default:
                return DateUtil.beginOfDay(DateUtil.offsetDay(date, offset));
        }
    }

    public static Map<String, DateTime> rangeDayToMap(Date start, Date end) {
        return DateUtil.rangeToList(start, end, DateField.DAY_OF_MONTH)
                .stream()
                .collect(Collectors.toMap(
                        DateTime::toDateStr,
                        v -> v,
                        (k, v) -> k,
                        HashMap::new
                ));
    }

    /**
     * 获取 unix时间戳
     *
     * @return
     */
    public static Long getUnix() {
        return Instant.now().getEpochSecond();
    }

    /*
     *<p>判断输入时间是否为限制时间范围内的偶数时间
     * 此方法用于确定给定的时间是否在特定范围内且为偶数小时，不同用户角色有不同的时间限制
     * 对于普通用户，时间范围是晚上6点到凌晨4点；对于管理员，时间范围是早10点到凌晨4
     * </p>
     *
     * @param time 要检查的时间点如果为null，则方法返回false
     * @param isAdmin 用户角色标识，true表示管理员，false表示普通用户
     * @return 如果时间在限制范围内且为偶数小时，则返回true；否则返回false
     *
     * @author gh
     * @date 2024/12/31 下午5:50
     */
    public static boolean isInTimeRangeAndEvenHour(LocalDateTime time, boolean isManager) {
        // 检查输入时间是否为null
        if (Objects.isNull(time)){
            return false;
        }
        if (!TimeUtil.isWholeHour(time)){
            return false;
        }
        Date date = convertToDate(time);

        // 获取输入时间的小时部分
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);

        // 判断是否为偶数小时
        boolean isEvenHour = hour % 2 == 0;
        // 判断时间是否为未来时间
        if (time.isBefore(LocalDateTime.now())) {
            return false;
        }
        // 根据用户角色判断是否在限制时间内
        if (isManager) {
            // 管理员的限制时间范围是早上10点到凌晨4点
            return isEvenHour && (hour >= 10 || hour <= 4);
        } else {
            // 普通用户的限制时间范围是晚上6点到凌晨4点
            return isEvenHour && (hour >= 18 || hour <= 4);
        }
    }

    public static boolean isWithinFiveMinutes(LocalDateTime targetTime) {
        LocalDateTime now = LocalDateTime.now();
        Duration duration = Duration.between(now, targetTime);
        long differenceInMinutes = Math.abs(duration.toMinutes());
        return differenceInMinutes <= 5;
    }

    /**
     * 判断时间是否为整数时间
     *
     * @param appointmentTime 目标时间
     * @return 如果是整数时间返回true，否则返回false
     */
    public static boolean isWholeHour(LocalDateTime appointmentTime) {
        return appointmentTime.getMinute() == 0 && appointmentTime.getSecond() == 0;
    }
}
